package com.declum.archon.chunkserver.manager.disk.streams;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * It acts as a Input Stream for chunks
 * 
 * @author Clement
 * 
 */
public class ChunkInputStream extends InputStream {
	public static int	NORMAL_MODE					= 0;
	public static int	VERIFIED_MODE				= 1;

	int					mode						= ChunkInputStream.NORMAL_MODE;
	File				file						= null;
	File				checkSum					= null;
	FileInputStream		chunkIn;
	FileInputStream		checkIn;
	byte[]				buffer;
	int[]				checkSumValues;
	private int			bufferSize					= 1024;
	private int			chunkSize					= 64 * 1024 * 1024;

	int					currentPossitionInFile		= 0;
	int					currentPossitionInBuffer	= 0;
	int					dataCountInBuffer			= 0;

	public int getBufferSize() {
		return this.bufferSize;
	}

	public void setBufferSize(int bufferSize) {
		throw new RuntimeException();
		// this.bufferSize = bufferSize;
	}

	public ChunkInputStream(String chunkPath) throws IOException {
		this(chunkPath, ChunkInputStream.NORMAL_MODE);
	}

	public ChunkInputStream(String chunkPath, int mode) throws IOException {
		this(new File(chunkPath), mode);
	}

	public ChunkInputStream(File chunkFile) throws IOException {
		this(chunkFile, ChunkInputStream.NORMAL_MODE);
	}

	public ChunkInputStream(File chunkFile, int mode) throws IOException {
		this.mode = mode;
		this.loadChunkFile(chunkFile);
	}

	/**
	 * It loads the Chunk file
	 * 
	 * @param chunkFile
	 * @throws IOException
	 */
	void loadChunkFile(File chunkFile) throws IOException {

		FileUtils.existsAndBeFile(chunkFile);
		this.file = chunkFile;
		this.chunkIn = new FileInputStream(this.file);
		this.buffer = new byte[this.bufferSize];

		if (this.mode == ChunkInputStream.VERIFIED_MODE) {
			File checkSumFile = new File(FileUtils
					.getCheckSumFileNameFromChunkPath(chunkFile
							.getAbsolutePath()));
			FileUtils.existsAndBeFile(checkSumFile);
			if (this.mode == ChunkInputStream.VERIFIED_MODE) {
				this.checkSum = checkSumFile;
			}
			this.checkIn = new FileInputStream(this.checkSum);
			this.checkSumValues = new int[this.calculateTotalRecordsPerChunk()];
			DataInputStream in = new DataInputStream(new FileInputStream(
					checkSumFile));
			int value;
			int i = 0;
			do {
				try {
					value = in.readInt();
					this.checkSumValues[i++] = value;
				} catch (Exception e) {
					break;
				}
			} while (true);
		}
	}

	/**
	 * Calculates the Total Records per chunk
	 * 
	 * @return
	 */
	private int calculateTotalRecordsPerChunk() {
		int total = 0;
		total = this.chunkSize / (this.bufferSize);
		return total;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.io.InputStream#read()
	 */
	@Override
	public int read() throws IOException {
		this.fillBufferIfNotEnough();
		if (this.dataCountInBuffer == -1) {
			return -1;
		}
		int read = this.buffer[this.currentPossitionInBuffer];
		this.currentPossitionInBuffer += 1;
		return read;
	}

	/**
	 * It fills the buffer if its not enough for reading
	 * 
	 * @throws IOException
	 */
	void fillBufferIfNotEnough() throws IOException {
		if (this.currentPossitionInBuffer == this.dataCountInBuffer) {
			this.dataCountInBuffer = this.chunkIn.read(this.buffer);
			if (this.mode == ChunkInputStream.VERIFIED_MODE) {
				int offset = this.currentPossitionInFile / this.bufferSize;
				int chuckSumValue = this.checkSumValues[offset];
				if (FileUtils.getCheckSumFotBuffer(this.buffer) != chuckSumValue) {
					throw new IOException("Checksum does not match");
				}
			}
			this.currentPossitionInFile += this.dataCountInBuffer;
		}
	}

}
